/**
 * Huize Service Inc
 * All Rights Reserved @2018
 */

package com.huize.zhike.framework.common.util;

import com.huize.ladon.apm.common.util.DateTimeUtils;
import com.huize.zhike.framework.common.enumeration.DateUnitEnum;
import org.apache.commons.lang3.StringUtils;

import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.Period;
import java.time.ZoneId;
import java.util.Calendar;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 描述: 时间 日期 工具类
 *
 * @author tianyuliang
 * @version $Id: DatetimeUtils.java, v0.1 2020/3/19 20:07 tianyuliang Exp $$
 */
@SuppressWarnings("deprecation")
public class DatetimeUtils extends DateTimeUtils {

    public static final ZoneId ZONE_ID_SHANGHAI = ZoneId.of("Asia/Shanghai");

    public static final Pattern PTN_YYYY_MM_DD_HH_MM_SS = Pattern.compile("^\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2}\\.\\d$");

    /**
     * 1天
     */
    public static final int ONE_DAY = 1;

    /**
     * 1周=7天
     */
    public static final int ONE_WEEK_DAY = 7;

    /**
     * 标准month格式
     */
    public final static String PATTERN_MONTHS = "yyyy-MM";

    /**
     * 扩展date格式
     */
    public final static String PATTERN_EXT_DATE = "yyyyMMdd";

    /**
     * 标准week格式
     */
    public final static String PATTERN_WEEK = "yyyy-ww";

    /**
     * 1秒-毫秒数
     */
    public static final Long ONE_SECOND = 1 * 1000L;

    /**
     * 5分钟-毫秒数
     */
    public static final Long FIVE_MINUTE = 5 * 60 * 1000L;

    /**
     * 每分钟-秒数
     */
    public static final Long ONE_MINUTE_SECOND = 1 * 60 * ONE_SECOND;

    /**
     * 每小时-秒数
     */
    public static final Long ONE_HOUR_SECOND = 1 * 60 * 60 * ONE_SECOND;

    /**
     * 每天-秒数
     */
    public static final Long ONE_DAY_SECOND = 1 * 60 * 60 * 24L;

    /**
     * 每天-毫秒数
     */
    public static final Long ONE_DAY_MILLISECOND = 1 * 60 * 60 * 24 * 1000L;

    /**
     * 拓展time格式
     */
    public final static String PATTERN_EXT_TIME = "yyyyMMdd-HHmmss";

    /**
     * 拓展time格式 yyyyMMdd-HHmmss
     */
    public static ThreadLocal<SimpleDateFormat> extTimeLocal = new ThreadLocal<SimpleDateFormat>() {
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat(PATTERN_EXT_TIME);
        }
    };

    /**
     * 标准week格式 yyyy-ww
     */
    public static ThreadLocal<SimpleDateFormat> weekLocal = new ThreadLocal<SimpleDateFormat>() {
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat(PATTERN_WEEK);
        }
    };

    /**
     * date格式 yyyyMMdd
     */
    public static ThreadLocal<SimpleDateFormat> monthExtDate = new ThreadLocal<SimpleDateFormat>() {
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat(PATTERN_EXT_DATE);
        }
    };

    /**
     * 按照时间颗粒度，将时间右侧补齐
     * “2020-04-10 23:33”   ---> “2020-04-10 23:33:00”
     * “2020-04-10 23”      ---> “2020-04-10 23:00:00”
     * “2020-04-10”         ---> “2020-04-10 00:00:00”
     *
     * @param datetime
     * @param unitEnum
     * @return
     */
    public static String rightPad(String datetime, DateUnitEnum unitEnum) {
        String value = datetime;
        if (DateUnitEnum.isMinuteType(unitEnum)) {
            value += ":00";
        } else if (DateUnitEnum.isHourType(unitEnum)) {
            value += ":00:00";
        } else if (DateUnitEnum.isDayType(unitEnum)) {
            value += " 00:00:00";
        }
        return value;
    }

    /**
     * 按week将date日期格式化为yyyy-ww
     *
     * @param date
     * @return
     */
    public static String formatWeek(Date date) {
//        Calendar calendar = Calendar.getInstance();
//        calendar.setTime(date);
//        calendar.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
//        String value = weekLocal.get().format(calendar.getTime());
//        return value;

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.DAY_OF_WEEK, calendar.SUNDAY);
        String value = weekLocal.get().format(date);
        return value;
    }

    /**
     * 判断字符串(yyyy-MM)能否转化为Date，返回true标识能够转化
     *
     * @param value
     * @return
     */
    public static boolean canParseMonth(String value) {
        Date date = parseCustomDate(value, PATTERN_MONTHS);
        return date != null;
    }

    /**
     * 将日期格式化为月份
     *
     * @param date
     * @return
     */
    public static String formatMonth(Date date) {
        return formatCustomDate(date, PATTERN_MONTHS);
    }

    /**
     * 将Date时间格式化为"yyyyMMdd-HHmmss"拓展时间
     *
     * @param time
     * @return
     */
    public static String formatExtTime(Date time) {
        if (time == null) {
            return "";
        }
        return extTimeLocal.get().format(time);
    }

    /**
     * 将字符串"yyyyMMdd-HHmmss"转化为Date时间
     *
     * @param extDateTime
     * @return
     */
    public static Date parseExtTime(String extDateTime) {
        try {
            SimpleDateFormat timeFormat = extTimeLocal.get();
            Date date = timeFormat.parse(extDateTime);
            return date;
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 校验字符串"yyyyMMdd-HHmmss"能否转化为Date时间
     *
     * @param extDateTime
     * @return
     */
    public static boolean canParseExtTime(String extDateTime) {
        try {
            SimpleDateFormat timeFormat = extTimeLocal.get();
            Date date = timeFormat.parse(extDateTime);
            return date != null;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 将字符串转化为Date，转化过程优先适配"yyyy-MM-dd HH:mm:ss"， 失败后再次适配"yyyy-MM-dd"
     *
     * @param strTime
     * @return
     */
    public static Date parseDateTime(String strTime) {
        Date date = parseTime(strTime);
        if (date == null) {
            date = parseDate(strTime);
        }
        return date;
    }

    /**
     * 将字符串(yyyyMMdd)转化为Date
     *
     * @param strDate
     * @return
     */
    public static Date parseExtDate(String strDate) {
        Date date = null;
        try {
            SimpleDateFormat timeFormat = monthExtDate.get();
            date = timeFormat.parse(strDate);
        } catch (Exception e) {
            return null;
        }
        return date;
    }


    /**
     * 将Date格式化为字符串(yyyyMMdd)
     *
     * @param date
     * @return
     */
    public static String formatExtDate(Date date) {
        if (date == null) {
            return "";
        }
        SimpleDateFormat dateFormat = monthExtDate.get();
        return dateFormat.format(date);
    }

    /**
     * 计算2个日期差异，单位秒数
     *
     * @param startDate
     * @param endDate
     * @return
     */
    public static Long diffSecond(Date startDate, Date endDate) {
        long t0 = startDate.getTime();
        long t1 = endDate.getTime();
        long diff = t1 - t0;
        Long day = Double.valueOf(diff * 1D / ONE_SECOND).longValue();
        return day;
    }

    /**
     * 计算2个日期差异，单位:月份
     *
     * @param fromDate
     * @param toDate
     * @return
     */
    public static int diffMonth(String fromDate, String toDate) {
        return diffMonth(parseDate(fromDate), parseDate(toDate));
    }

    /**
     * 计算2个日期差异，单位:月份
     *
     * @param fromDate
     * @param toDate
     * @return
     */
    public static int diffMonth(Date fromDate, Date toDate) {
        if (fromDate == null || toDate == null) {
            throw new IllegalArgumentException("fromDate或者toDate参数无效!");
        }
        LocalDate startDate = fromDate.toInstant().atZone(ZONE_ID_SHANGHAI).toLocalDate();
        LocalDate endDate = toDate.toInstant().atZone(ZONE_ID_SHANGHAI).toLocalDate();
        Period period = Period.between(startDate, endDate);
        int diffMonth = period.getMonths();
        return diffMonth;
    }

    /**
     * 计算2个日期差异,单位:小时数
     *
     * @param startDate
     * @param endDate
     * @return
     */
    public static int diffHour(Date startDate, Date endDate) {
        long t0 = startDate.getTime();
        long t1 = endDate.getTime();
        long diff = t1 - t0;
        int hour = Double.valueOf(diff * 1D / ONE_HOUR_SECOND).intValue();
        return hour;
    }

    /**
     * 计算2个日期差异,单位:天数
     *
     * @param strStart
     * @param strEnd
     * @return
     */
    public static int diffDay(String strStart, String strEnd) {
        Date startDate = parseDate(strStart);
        Date endDate = parseDate(strEnd);
        long t0 = startDate.getTime();
        long t1 = endDate.getTime();
        long diff = t1 - t0;
        int day = Double.valueOf(diff * 1D / ONE_DAY_MILLISECOND).intValue();
        return day;
    }

    /**
     * 计算2个日期差异，单位:周数
     *
     * @param strStart
     * @param strEnd
     * @return
     */
    public static int diffWeek(String strStart, String strEnd) {
        int day = diffDay(strStart, strEnd);
        int week = Double.valueOf(day * 1D / ONE_WEEK_DAY).intValue();
        return week;
    }

    /**
     * 计算2个日期差异单位分钟数
     *
     * @param startDate
     * @param endDate
     * @return
     */
    public static int diffMinute(Date startDate, Date endDate) {
        long t0 = startDate.getTime();
        long t1 = endDate.getTime();
        long diff = t1 - t0;
        int minute = Double.valueOf(diff * 1D / ONE_MINUTE_SECOND).intValue();
        return minute;
    }

    /**
     * 获取currentDate时间增加value秒的值
     *
     * @param currentDate
     * @param value
     * @return
     */
    public static Date getNextSecond(Date currentDate, Long value) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(currentDate);
        calendar.add(Calendar.SECOND, value.intValue());
        return calendar.getTime();
    }

    /**
     * 获取currentDate时间减去value月的值
     *
     * @param currentDate
     * @param value
     * @return
     */
    public static Date getPreMonth(Date currentDate, Integer value) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(currentDate);
        calendar.add(Calendar.MONTH, -value);
        return calendar.getTime();
    }

    /**
     * 获取currentDate时间减去value年的值
     *
     * @param currentDate
     * @param value
     * @return
     */
    public static Date getPreYear(Date currentDate, Integer value) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(currentDate);
        calendar.add(Calendar.YEAR, -value);
        return calendar.getTime();
    }

    /**
     * 获取指定时间往后第X天
     *
     * @param currentDate
     * @param x
     * @return
     */
    public static Date getNextDay(Date currentDate, int x) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(currentDate);
        calendar.add(Calendar.DATE, x);
        return calendar.getTime();
    }

    /**
     * 获取指定时间往后第X月
     *
     * @param currentDate
     * @param x
     * @return
     */
    public static Date getNextMonth(Date currentDate, int x) {
        if (x == 0) {
            return currentDate;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(currentDate);
        calendar.add(Calendar.MONTH, x);
        return calendar.getTime();
    }


    /**
     * 获取指定日期的第0时0分0秒
     *
     * @param date 指定时间
     * @return
     */
    public static Date getDateZeroHour(Date date) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.set(Calendar.MINUTE, 0);
        c.set(Calendar.SECOND, 0);
        c.set(Calendar.MILLISECOND, 0);
        return c.getTime();
    }

    /**
     * 获取当前时间对应分钟的第0秒
     *
     * @param date 指定时间
     * @return
     */
    public static Date getDateZeroSecond(Date date) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.set(Calendar.MILLISECOND, 0);
        return c.getTime();
    }

    /**
     * 获取当前时间对应小时的第0分0秒
     *
     * @param date 指定时间
     * @return
     */
    public static Date getDateZeroMinute(Date date) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.set(Calendar.SECOND, 0);
        c.set(Calendar.MILLISECOND, 0);
        return c.getTime();
    }


    /**
     * 获取时分秒对应0的时间
     *
     * @param date
     * @param unitEnum
     * @return
     */
    public static Date convertZeroDate(Date date, DateUnitEnum unitEnum) {
//        if (unitEnum == DateUnitEnum.SECOND) {
//            return getDateZeroSecond(date);
//        } else
        if (unitEnum == DateUnitEnum.MINUTE) {
            return getDateZeroMinute(date);
        } else if (unitEnum == DateUnitEnum.HOUR) {
            return getDateZeroHour(date);
        }
        return date;
    }


    /**
     * 按时间颗粒度转化Date
     *
     * @param value
     * @return
     */
    public static Date parseCustomDateUnit(String value) {
        return parseCustomDate(value, DateUnitEnum.DAY);
    }

    /**
     * 按时间颗粒度转化Date
     *
     * @param value
     * @param
     * @return
     */
    public static Date parseAdapterDate(String value) {
        if (canParseTime(value)) {
            return parseTime(value);
        } else if (canParseDate(value)) {
            return parseDate(value);
        } else {
            throw new RuntimeException("时间颗粒度unitEnum参数异常,无法转化时间" + value);
        }
    }

    /**
     * 按时间颗粒度转化Date
     *
     * @param value
     * @param unitEnum
     * @return
     */
    public static Date parseCustomDate(String value, DateUnitEnum unitEnum) {
        if (canParseTime(value)) {
            return parseTime(value);
        } else if (canParseDate(value)) {
            return parseDate(value);
        } else {
            throw new RuntimeException("时间颗粒度unitEnum参数异常,无法转化时间" + value);
        }
    }

    public static Date fixDayUnit(Date toDate, DateUnitEnum unitEnum) {
        if (DateUnitEnum.isDayType(unitEnum)) {
            Date currentDate = new Date();
            String current = formatDate(currentDate);
            String to = formatDate(toDate);
            boolean isCurrentDay = current.equals(to);

            String toTime = formatTime(toDate);
            String toHourTime = toTime.split(" ")[1];
            boolean isHourZero = toHourTime.equalsIgnoreCase("00:00:00");

            if (isHourZero) {
                if (isCurrentDay) {
                    // 时分秒未设置,都是00:00:00,而结束日期恰好是今天，则返回当前时间所在的时分秒
                    return currentDate;
                } else {
                    // 时分秒未设置,都是00:00:00,而结束日期不是今天，则需要设置时分秒为23:59:59，返回修正后的时分秒
                    Date endDate = getDateWithMaxMinute(toDate, 23);
                    return endDate;
                }
            }
        }

        // 按天查询，如果结束时间不是00:00:00，说明已经设置时分秒,则不需要修正
        // 不是按天返回,则直接返回原值,不需要修正
        return toDate;
    }

    /**
     * 将时间字符串改成标准格式
     * 例如  source=2017-07-25 14:53:53.0， 调用后 target=2017-07-25 14:53:53
     *
     * @param source 符合样式PTN_YYYY_MM_DD_HH_MM_SS的时间字符串
     * @return 去掉秒后面的数字
     */
    public static String toRegexDateTime(String source) {
        if (StringUtils.isEmpty(source)) {
            return null;
        }
        String target = source;
        try {
            Matcher matcher = PTN_YYYY_MM_DD_HH_MM_SS.matcher(source);
            if (matcher.matches()) {
                target = StringUtils.replacePattern(source, "\\.\\d$", "");
            }
        } catch (Exception e) {
            // ignore
        }
        return target;
    }

    public static String buildWeekDay(String strDate) {
        Date date = parseDate(strDate);
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        int week = calendar.get(Calendar.DAY_OF_WEEK);
        String weekDay = "";
        /*星期日:Calendar.SUNDAY=1
         *星期一:Calendar.MONDAY=2
         *星期二:Calendar.TUESDAY=3
         *星期三:Calendar.WEDNESDAY=4
         *星期四:Calendar.THURSDAY=5
         *星期五:Calendar.FRIDAY=6
         *星期六:Calendar.SATURDAY=7 */
        switch (week) {
            case 1:
                weekDay = "周日";
                break;
            case 2:
                weekDay = "周一";
                break;
            case 3:
                weekDay = "周二";
                break;
            case 4:
                weekDay = "周三";
                break;
            case 5:
                weekDay = "周四";
                break;
            case 6:
                weekDay = "周五";
                break;
            case 7:
                weekDay = "周六";
                break;
            default:
                break;
        }
        return weekDay;
    }

    public static void main(String[] args) {
        System.out.println(buildWeekDay("2020-09-16"));

        Date now = new Date();
        System.out.println(format(now, "yyyy-MM-dd HH:00:00"));

        Date otherDate = getPreHour(now, 3);
        System.out.println(format(otherDate, "yyyy-MM-dd HH:00:00"));

        Date toDate11 = parseDate("2020-06-27");
        Date toFixDate11 = fixDayUnit(toDate11, DateUnitEnum.DAY);
        System.out.println(formatTime(toFixDate11));

        Date toDate22 = parseTime("2020-06-28 10:07:08");
        Date toFixDate22 = fixDayUnit(toDate22, DateUnitEnum.DAY);
        System.out.println(formatTime(toFixDate22));

        Date toDate33 = parseTime("2020-06-27 10:07:08");
        Date toFixDate33 = fixDayUnit(toDate33, DateUnitEnum.DAY);
        System.out.println(formatTime(toFixDate33));

        Date toDate44 = parseTime("2020-06-27 00:00:00");
        Date toFixDate44 = fixDayUnit(toDate44, DateUnitEnum.DAY);
        System.out.println(formatTime(toFixDate44));

        System.out.println(diffDay("2020-03-15", "2020-03-18"));
    }

}
